package com.excel;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

/**
 * @author wuxz
 * @create 2022-05-28 11:02
 */
public class ExcelUtil {


    /**
     * 文件流输出EXCEL
     *
     * @param request
     * @param response
     * @param list
     */
    public static <T> void downloadExcel(HttpServletRequest request, HttpServletResponse response, Class<T> tClass, List<T> list, String sheetName) throws IOException {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        String filename = URLEncoder.encode(sheetName, "utf-8");
        response.setHeader("Content-disposition", "attachment;filename=" +
                filename + ".xlsx");
        // 写入数据到excel
        ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), tClass).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();
        WriteSheet detailSheet = EasyExcel.writerSheet(0, sheetName).build();
        excelWriter.write(list, detailSheet);
        excelWriter.finish();
    }

    /**
     * 文件流输出EXCEL(排除指定字段)
     *
     * @param request
     * @param response
     * @param excludeColumnFiledNames 排除字段集合
     * @param list
     */
    public static <T> void downloadExcelExcludeFields(HttpServletRequest request, HttpServletResponse response, Class<T> tClass, Set<String> excludeColumnFiledNames, List<T> list, String sheetName) throws IOException {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        String filename = URLEncoder.encode(sheetName, "utf-8");
        response.setHeader("Content-disposition", "attachment;filename=" +
                filename + ".xlsx");
        // 写入数据到excel
        ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), tClass).excludeColumnFiledNames(excludeColumnFiledNames).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();
        WriteSheet detailSheet = EasyExcel.writerSheet(0, sheetName).build();
        excelWriter.write(list, detailSheet);
        excelWriter.finish();
    }


    /**
     * 获取读取Excel的监听器对象
     * 为了解耦及减少每个数据模型bean都要创建一个监听器的臃肿, 使用泛型指定数据模型类型
     * 使用jdk8新特性中的函数式接口 Consumer
     * 可以实现任何数据模型bean的数据解析, 不用重复定义监听器
     *
     * @param consumer  处理解析数据的函数, 一般可以是数据入库逻辑的函数
     * @param threshold 阈值,达到阈值就处理一次存储的数据
     * @param <T>       数据模型泛型
     * @return 返回监听器
     */
    public static <T> AnalysisEventListener<T> getReadListener(Consumer<List<T>> consumer, int threshold) {

        return new AnalysisEventListener<T>() {

            /**
             * 存储解析的数据 T t
             */
            // ArrayList基于数组实现, 查询更快
//            List<T> dataList = new ArrayList<>(threshold);
            // LinkedList基于双向链表实现, 插入和删除更快
            List<T> dataList = new LinkedList<>();

            /**
             * 每解析一行数据事件调度中心都会通知到这个方法, 订阅者1
             * @param data 解析的每行数据
             * @param context
             */
            @Override
            public void invoke(T data, AnalysisContext context) {
                dataList.add(data);
                // 达到阈值就处理一次存储的数据
                if (dataList.size() >= threshold) {
                    consumer.accept(dataList);
                    dataList.clear();
                }
            }

            /**
             * excel文件解析完成后,事件调度中心会通知到该方法, 订阅者2
             * @param context
             */
            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                // 最后阈值外的数据做处理
                if (dataList.size() > 0) {
                    consumer.accept(dataList);
                }
            }
        };

    }

    /**
     * 获取读取Excel的监听器对象, 不指定阈值, 默认阈值为 2000
     *
     * @param consumer
     * @param <T>
     * @return
     */
    public static <T> AnalysisEventListener<T> getReadListener(Consumer<List<T>> consumer) {
        return getReadListener(consumer, 100);
    }


}
